from .BaseMQ import Init, OpenConnRabbitMQ, CloseConnRabbitMQ


class MQRecvMessage(object):
    def __init__(self, cofig, queuetest):
        # 执行init 使全局变量赋值
        Init(cofig)
        # 连接mq
        self.ConnRabbitMQ = OpenConnRabbitMQ()
        # 创建频道
        self.channel = self.ConnRabbitMQ.channel()
        # 设置队列名
        self.queue = queuetest
        # 交换区方式 共4种
        self.exchang = ""
        # 某些交换区模式需要用到
        self.routing_key = ""
        # no_ack 如果为True 将关闭消息确认，如果为False 需要 ch.basic_ack(delivery_tag=method.delivery_tag)
        self.no_ack = True
        # 默认的交换区模式
        self.nullExchange()

    def OpenConn(self):
        # 连接失效后调用该方法
        self.ConnRabbitMQ = OpenConnRabbitMQ()

    def __del__(self):
        # 类被删除时使用
        self.channel.close()
        self.ConnRabbitMQ.close()

    '''
    如果RabbitMQ服务停止了的话，我们的任务还是会丢失。
    当RabbitMQ退出或宕掉的话将会丢失queues和消息信息，除非你进行设置告诉服务器队列不能丢失。要确保消息不会丢失需要做两件事： 我们需要将队列和消息标记为 durable
    首先：我们需要确保RabbitMQ 永远不会丢失队列，为了确保这个，我们需要定义队列为durable：
    加入durable=True属性可以确保服务器队列不丢失
    我们已经定义了个名为 hello ，但不是durable属性的队列。
    RabbitMQ不允许你重新定义一个已经存在、但属性不同的queue。
    RabbitMQ 将会给定义这个属性的程序返回一个错误。
    但这里有一个快速的解决方法：让我们定义个不同名称的队列，比如 task_queue:
    '''
    def setQueue(self, Temporaryqueue=False):
        # 临时队列
        if Temporaryqueue:
            # 无论什么时候我们连接到Rabbit我们需要一个空的、新的队列。为了实现这个我们可以创建个随机名的队列，
            # 或者，更好的 - 让服务端选择一个随机的队列名给我们，
            # 我们可以不给queue_declare方法设置queue参数来实现。

            # 一旦我们断开consumer连接，这个队列名将自动删除。这里有一个标识设置：
            result = self.channel.queue_declare(exclusive=True, durable=True)
            self.queue = result.method.queue
            print("queue name is:" + result.method.queue)
            return result
        else:
            # 加入durable=True属性可以确保服务器队列不丢失
            self.channel.queue_declare(self.queue, durable=True)  # 声明或创建队列
        return None

    def setExchange(self, exchange, type):
        print(type)
        self.channel.exchange_declare(exchange=exchange, exchange_type=type)

    def fanoutExchange(self, exchange):
        """
        fanout exchange非常简单，你从这个名字中就能猜出来，它将从Producer方收到的消息广播给所有
        他知道的receiver方。而这正是我们的logger记录所需要的消息。
        :param exchange:
        :return:
        """
        self.exchang = exchange
        self.routing_key = ""
        self.setExchange(exchange, "fanout")

    def DirectExchange(self, exchange):
        """
        直接交换
        通过绑定的key确定分发到哪个队列
        :return:
        """
        self.exchang = exchange
        self.routing_key = ""
        self.setExchange(exchange, "direct")

    def TopicExchange(self, exchange):
        self.exchang = exchange
        self.routing_key = ""
        self.setExchange(exchange, "topic")

    def nullExchange(self):
        """
        这个exchange参数就是这个exchange的名字. 空字符串标识默认的或者匿名的exchange：
        如果存在routing_key, 消息路由到routing_key指定的队列中。
        :param exchange:
        :return:
        """
        self.exchang = ""
        self.routing_key = self.queue

    def queueBind(self, routing_key=None):
        """
        服务器分配的队列queue = result.method.queue
        :param queue:
        :param routing_key 在直接交换模式下使用
        :return:
        """
        if isinstance(routing_key, str):
            self.channel.queue_bind(exchange=self.exchang,
                                    queue=self.queue,
                                    routing_key=routing_key)
        elif isinstance(routing_key, list):
            for key in routing_key:
                self.channel.queue_bind(exchange=self.exchang,
                                        queue=self.queue,
                                        routing_key=key)
        else:
            raise ValueError("传入的routing_key 值错误")

    def callback(self, ch, method, properties, body):
        if not self.no_ack:
            ch.basic_ack(delivery_tag=method.delivery_tag)

    def consume(self):
        # 进行消息确认
        self.channel.basic_consume(self.callback,
                                   queue=self.queue,
                                   no_ack=self.no_ack)

    def basic_get(self):
        return self.channel.basic_get(self.queue)

    def qos(self, prefetch_count=1):
        # 消息未处理完前不要发送信息的消息,不然服务器会不停的发送消息而不管是否处理完
        self.channel.basic_qos(prefetch_size=0, prefetch_count=prefetch_count, all_channels=True)

    def start(self):
        """
        #我们执行永不停止的循环来等待数据和调用callbacks进行数据处理
        启动函数  最后调用
        :return:
        """
        self.channel.start_consuming()

    def close(self):
        CloseConnRabbitMQ(self.ConnRabbitMQ)

# if __name__ == "__main__":
#     mqsm = MQRecvMessage("../config/config.ini", "queuetest")
#     mqsm.no_ack = False
#     mqsm.setQueue(True)
#     mqsm.fanoutExchange("topic_logs")
#     mqsm.queueBind("kern.*")
#     mqsm.qos()
#     mqsm.consume()
#     mqsm.start()
#     mqsm.close()
